import time
from typing import List, Optional

from fastapi import Request
from fastapi_pagination.bases import AbstractPage
from fastapi_pagination.ext.databases import paginate
from sqlalchemy import insert, select, update

from forward.dependencies.database import db
from forward.api.h5.schemas.article import (
    ArticleCollectSchema,
    ArticleDetailOutSchema,
    ArticleListInSchema,
    ArticleDetailInSchema,
    ArticleCategoryOutSchema,
    ArticleBaseOutSchema,
    ArticleCollectPostInSchema,
    ArticleCollectOutSchema,
)
from forward.model.tables.article import (
    ArticleCateGoryModel,
    ArticleCollectModel,
    ArticleModel,
)
from forward.utils.urls import UrlUtil


class ArticleService:
    """
    文章服务实现类
    """

    article_order_by = [ArticleModel.sort.desc(), ArticleModel.id.desc()]
    cate_order_by = [ArticleCateGoryModel.sort.desc(), ArticleCateGoryModel.id.desc()]
    collect_order_by = [ArticleCollectModel.id.desc()]

    def __init__(self, request: Request):
        self.request: Request = request
        self.user_id = getattr(request.state, "user_id", None)

    async def get_user_collect_article_ids(
        self, user_id: int, article_ids: Optional[List[int]]
    ) -> List[int]:
        """
        返回用户收藏的文章ID
        :param user_id:
        :param article_ids:
        :return:
        """
        where = [
            ArticleCollectModel.user_id == user_id,
            ArticleCollectModel.is_delete == False,
        ]
        if article_ids:
            where.append(ArticleCollectModel.article_id.in_(article_ids))
        query = (
            select(ArticleCollectModel).select_from(ArticleCollectModel).where(*where)
        )
        collect_records = await db.fetch_all(query)
        collect_acticle_ids = [
            ArticleCollectSchema.model_validate(
                collect, from_attributes=True
            ).article_id
            for collect in collect_records
        ]

        return collect_acticle_ids

    async def update_article_visit(self, article_id: int):
        """
        文章访问数 +1
        :param article_id:
        :return:
        """
        return await db.execute(
            update(ArticleModel)
            .where(ArticleModel.id == article_id)
            .values(visit=ArticleModel.visit + 1)
        )

    async def category(self) -> List[ArticleCategoryOutSchema]:
        """
        文章分类列表
        :return:
        """
        where = [ArticleCateGoryModel.is_delete == 0, ArticleCateGoryModel.is_show == 1]
        query = (
            select(ArticleCateGoryModel.id, ArticleCateGoryModel.name)
            .select_from(ArticleCateGoryModel)
            .where(*where)
            .order_by(*self.cate_order_by)
        )
        all_cate = await db.fetch_all(query)

        return [
            ArticleCategoryOutSchema.model_validate(cate, from_attributes=True)
            for cate in all_cate
        ]

    async def list(
        self, list_in: ArticleListInSchema
    ) -> AbstractPage[ArticleBaseOutSchema]:
        """
        文章列表
        :param list_in:
        :return:
        """
        where = [ArticleModel.is_delete == 0, ArticleModel.is_show == 1]
        if list_in.cid != 0:
            where.append(ArticleModel.cid == list_in.cid)
        if list_in.keyword is not None:
            where.append(ArticleModel.title.like("%{0}%".format(list_in.keyword)))
        order_by = self.article_order_by

        if list_in.sort:
            if list_in.sort == "hot":
                order_by = [ArticleModel.visit.desc(), ArticleModel.id.desc()]
            elif list_in.sort == "new":
                order_by = [ArticleModel.id.desc()]

        query = (
            select(ArticleModel)
            .select_from(ArticleModel)
            .where(*where)
            .order_by(*order_by)
        )
        page_result = await paginate(db, query)
        # 补充收藏信息/image转换为绝对地址
        article_ids = [row.id for row in page_result.lists]
        article_collects = []
        if self.user_id:
            article_collects = await self.get_user_collect_article_ids(
                user_id=self.user_id, article_ids=article_ids
            )
        for row in page_result.lists:
            row.image = await UrlUtil.to_absolute_url(row.image)
            if article_collects:
                row.collect = bool(row.id in article_collects)
        return page_result

    async def detail(self, detail_in: ArticleDetailInSchema) -> ArticleDetailOutSchema:
        """
        文章详情
        :param detail_in:
        :return:
        """
        article_detail_record = await db.fetch_one(
            select(ArticleModel)
            .select_from(ArticleModel)
            .where(ArticleModel.id == detail_in.id, ArticleModel.is_delete == 0)
            .limit(1)
        )
        assert article_detail_record, "数据不存在！"

        article_detail = ArticleDetailOutSchema.model_validate(
            article_detail_record, from_attributes=True
        )
        article_detail.image = await UrlUtil.to_absolute_url(article_detail.image)
        if self.user_id:
            article_collects = await self.get_user_collect_article_ids(
                user_id=self.user_id, article_ids=[article_detail.id]
            )
            article_detail.collect = bool(article_detail.id in article_collects)
        await self.update_article_visit(article_detail.id)
        return article_detail

    async def collect(self) -> AbstractPage[ArticleCollectOutSchema] | List:
        """
        收藏列表
        :return:
        """
        if not self.user_id:
            return []

        collect_query = (
            select(
                ArticleCollectModel.created_time,
                ArticleCollectModel.article_id,
                ArticleCollectModel.id,
                ArticleModel.visit,
                ArticleModel.title,
                ArticleModel.image,
                ArticleModel.intro,
            )
            .select_from(ArticleCollectModel)
            .outerjoin(ArticleModel, ArticleCollectModel.article_id == ArticleModel.id)
            .where(
                ArticleCollectModel.user_id == self.user_id,
                ArticleCollectModel.is_delete == False,
                ArticleModel.is_delete == False,
            )
            .order_by(ArticleCollectModel.id.desc())
        )

        collect_pages = await paginate(db, collect_query)
        for row in collect_pages.lists:
            row.image = await UrlUtil.to_absolute_url(row.image)
        return collect_pages

    async def add_collect(self, post_in: ArticleCollectPostInSchema):
        """
        添加收藏
        :param post_in
        :return:
        """
        article_collect_record = await db.fetch_one(
            select(
                ArticleCollectModel.id,
                ArticleCollectModel.article_id,
                ArticleCollectModel.user_id,
            )
            .select_from(ArticleCollectModel)
            .where(
                ArticleCollectModel.user_id == self.user_id,
                ArticleCollectModel.article_id == post_in.article_id,
            )
        )
        if article_collect_record:
            article_collect = ArticleCollectSchema.model_validate(
                article_collect_record, from_attributes=True
            )
            # 已有收藏记录
            return await db.execute(
                update(ArticleCollectModel)
                .where(ArticleCollectModel.id == article_collect.id)
                .values(is_delete=False)
            )
        else:
            # 新增收藏记录
            collect_create = {
                "user_id": self.user_id,
                "article_id": post_in.article_id,
            }

            return await db.execute(
                insert(ArticleCollectModel).values(**collect_create)
            )

    async def cancel_collect(self, post_in: ArticleCollectPostInSchema):
        """
        取消收藏
        :param post_in:
        :return:
        """
        article_collect_record = await db.fetch_one(
            select(ArticleCollectModel.id)
            .select_from(ArticleCollectModel)
            .where(
                ArticleCollectModel.user_id == self.user_id,
                ArticleCollectModel.is_delete == False,
                ArticleCollectModel.article_id == post_in.article_id,
            )
        )
        assert article_collect_record, "收藏不存在!"
        article_collect = ArticleCollectSchema.model_validate(
            article_collect_record, from_attributes=True
        )
        return await db.execute(
            update(ArticleCollectModel)
            .where(ArticleCollectModel.id == article_collect.id)
            .values(is_delete=True, update_time=int(time.time()))
        )

    @classmethod
    async def instance(cls, request: Request):
        """实例化"""
        return cls(request=request)
